We have identified a software design decision and an architectural decision which we believe are questionable. Despite being students currently learning about software architecture, we believe there are things that Gradle could've done better. Therefore, we humbly present the following criticism.
Across Gradle's developers have made the design decision to use interfaces for a vast majority of interactions between software elements. This is despite the fact that some of these interfaces are implemented by only one class and conceptually, are very unlikely to have other implementing classes in the future. An common pattern we observe throughout the code would be the presence of an interface "X", with a single implementation "DefaultX" or "XInternal".
Examples include PluginManagerInternal, TaskContainerInternal, and CommandLineActionFactory.
By polluting the source code with interfaces that serve no practical purpose, the code becomes extremely convoluted. This makes contribution to the source code by developers that are not initmately familiar with Gradle extremely difficult.
Overusing interfaces also makes using IDE tools difficult. For example, tools that show class hierarchies or find where a class is used / instantiated (the interface is instantiated instead). Understanding such a large codebase is difficult without those tools.
In our opinion, a better solution would be to only use interfaces when there are multiple implementing classes or if the interface is identified as a variation point (high likely-hood of change or future implementations). In the future, in the very rare cases where a class "X" was not properly identified as a variation point and thus did not implement an interface, simply rename the class to "DefaultX" and add the interface "X". Notice how very little change to the code is required.
It can be understood that this decision was in support of achieving greater flexibility for future development. In many cases however, this decision results in producing all the negative effects of using interfaces without any of the benefits. That is to say, more and more interfaces make the source code increasingly difficult to read and understand without any flexibility benefits.
We acknowledge that removing interfaces might make it slightly more difficult to create new implementing classes in the future, however we do not believe this warrants the overuse of interfaces we've observed across Gradle's codebase.
Gradle enforces a strict requirement on builds to have one settings file on top of a build.gradle file for each project. To use Gradle, we must first initialize the directory with "gradle init" this adds several additional files to the directory. For example, initializing the directory adds a gradle.properties, .gitattributes, .gitignore, gradlew, and gradlew.bat.
Generating an excessive amount of files makes it difficult to justify using gradle to build simple software. Even if the software follows a single-project structure, gradle requires a build.gradle file and settings.gradle file.
We believe that there should be a way to initialize Gradle for simple projects (no multi-project structure) such that a single file is generated (e.g. only the settings script file). The file could contain all the information relating to the settings, properties and project build configuration. This file would be similar to Maven's pom.xml file
Gradle uses the script files to achieve seperation of concerns. However, the amount of files generated is overwhelming and may scare away inexperienced developers from using Gradle as a build tool. Therefore, since inexperienced developers will not use Gradle, they will also not learn to use Gradle progressively, thus when they finally become experienced developers they will keep relying on simpler yet inefficient tools such as Maven (which they have been using all along).
Such a file will obviously incurr development cost since new script loaders must be written and the feature must be maintained.